/** 
Copyright (c) 2010, Sheikh Abdul Wahid Ahmed
Details @ http://extremecodeworld.codeplex.com/license
**/

using System;
using System.Xml;
using EXtremecode.Logging;
using System.Collections;
using EXtremecode.Utils;
using EXtremecode.Common;
using System.Text.RegularExpressions;
using System.Text;

namespace CodeGenerator.Definitions
{
	/// <summary>
	/// Summary description for Entity.
	/// </summary>
	public class Entity
	{
		XmlElement xmlElement;
		EntityCollection coll;
		Entity.FieldCollection fields;
		Entity.FieldCollection sortedFields;
		Entity.RelationCollection children,parents;
		Entity.Field[] primaryKey;
		Entity overridingEntity=null;
		public override string ToString()
		{
			return Name;
		}
		public override int GetHashCode()
		{
			return Name.GetHashCode();
		}



		public Entity(XmlElement xmlElement,EntityCollection coll)
		{
			this.xmlElement=xmlElement;
			this.coll=coll;
			fields=new FieldCollection(xmlElement,this);

			//---Relations
			//Children
			XmlNodeList childrenElements=xmlElement.GetElementsByTagName("children");
			children=new RelationCollection
				((childrenElements.Count ==0)? null: (childrenElements[0] as XmlElement),
				this,true);

			//Parents
			XmlNodeList parentsElements=xmlElement.GetElementsByTagName("parents");
			parents=new RelationCollection
				((parentsElements.Count ==0)? null: (parentsElements[0] as XmlElement),
				this,false);

		}

		public RelationCollection Children
		{
			get
			{
				return children;
			}
		}
		public RelationCollection Parents
		{
			get
			{
				return parents;
			}
		}

		public string AccessorName
		{
			get
			{
				return this["accessorName"];
			}
		}
		public string Name
		{
			get
			{
				return this["name"];
			}
		}
		public string TableName
		{
			get
			{
				return this["table"];
			}
		}
		public string ViewName
		{
			get
			{
				return this["view"];
			}
		}
		public string TypeCode
		{
			get
			{
				return this["typeCode"];
			}
		}

		public EntityCollection Collection
		{
			get
			{
				return coll;
			}
		}
		public string this[string key]
		{
			get
			{
				if(overridingEntity != null && overridingEntity[key] != string.Empty)
				{
					return overridingEntity[key];
				}
				else
				{
					return (xmlElement.Attributes[key]==null)? string.Empty:
						xmlElement.Attributes[key].Value;
				}
			}
		}

		public string this[string key, bool isAttributeList]
		{
			get
			{
				string str=this[key];
				if(!isAttributeList)
				{
					return str;
				}
				else
				{
					return StringProcessor.GetFormatedKeyValuePairs(str);	
				}
			}
		}
		public Entity.FieldCollection Fields
		{
			get
			{
				return fields; 
			}
		}

		public Entity.FieldCollection SortedFields
		{
			get
			{
				if(sortedFields == null)
				{
					sortedFields=new FieldCollection(this);
				}
				return sortedFields; 
			}
		}
		public string KeySignature
		{
			get
			{
				string signature=string.Empty;
				string qoma="";
				foreach(Entity.Field fld in PrimaryKey)
				{
					signature+=qoma + string.Format("{0} {1}",
						FreeHash.CSharpType[fld.DataType],
						StringProcessor.Variable(fld.Name));
						qoma=",";
						
				}
				return signature;
			}
		}
		public Entity.Field[]  PrimaryKey
		{
			get
			{
				if(primaryKey == null)
				{
					SortedList sl=new SortedList();
					foreach(string key in fields.Keys)
					{
						Entity.Field fld = fields[key];
						if(fld.IsKey==true)
						{
							sl.Add(fld.Name,fld);
						}
						
					}
					primaryKey =(Entity.Field[])
						new ArrayList(sl.Values).ToArray(typeof(Entity.Field));
					
				}
				return primaryKey;
			}
		}
		
		public Entity OverridingEntity 
		{
			get
			{
				return overridingEntity;
			}
			set
			{
				overridingEntity = value;
				sortedFields = new FieldCollection(this);
			}
		}

		//****Custom Entity Properties
		public int Ordinal
		{
			get
			{
				int i=0;
				try
				{
					i=Convert.ToInt32(this["ordinal"]);
				}
				catch{}
				return i;
			}
		}
		public string Caption
		{
			get
			{
				string str=this["caption"];
				if(str==string.Empty)
				{
					//default
					str= StringProcessor.Caption(Name);
				}
				return str;
			}
		}

		public string GridKey
		{
			get
			{
				string str=this["gridKey"];
				if(str==string.Empty)
				{
					//default
					string qoma=string.Empty;
					foreach(Entity.Field fld in PrimaryKey)
					{
						str += qoma + fld.Name;
						qoma=",";
					}

					if(str==string.Empty)
					{
						//if key is not defined for the perticular entity then sorted column will be 
						//the first indexed field in 'fields' collection.
						str=Fields[0].Name;
					}
				}
				return str;
			}
		}

		public string EditKey
		{
			get
			{
				return GridKey;
			}
		}
		

		public string GridSortedBy
		{
			get
			{
				string str=this["gridSortedBy"];
				if(str==string.Empty)
				{
					//default
					str=GridKey;
				}
				return str;
			}
		}

		public int FieldsCountInSingleRow
		{
			get
			{
				int i=2;//default
				try
				{
					i=Convert.ToInt32(this["fieldsCountInSingleRow"]);
				}
				catch{}
				return i;
			}
		}

		

		#region Inner Class [Relation]
		public class Relation
		{
			#region Inner Class [RelationalFields]
			public class RelationalFields  
			{
				XmlElement xmlElement;
				public RelationalFields(XmlElement xmlElement)
				{
					this.xmlElement=xmlElement;
				}

				public string  Native
				{
					get
					{
						return  this["nativeField"];
					}
				}
				public string Referenced
				{
					get
					{
						return this["referenceField"];
					}
				}
				public string this [string key]
				{
					get
					{
						return (xmlElement.Attributes[key]==null)? string.Empty:
							xmlElement.Attributes[key].Value;
					}
				}
			}

			#endregion

			XmlElement xmlElement;
			Entity entity;
			bool isParentNative;
			Relation.RelationalFields[] relationalFieldsArray;
			public Relation(XmlElement xmlElement,Entity entity,bool isParentNative)
			{
				this.xmlElement=xmlElement;
				this.entity=entity;
				this.isParentNative=isParentNative;


				//load relational Fields.
				XmlNodeList nodes=XmlUtil.GetNodes(xmlElement,
					"shk:relationalFields",Configurations.Provider.XMLN,"shk");  
				SortedList sl=new SortedList();
				foreach(XmlElement element in nodes)
				{
					Relation.RelationalFields relationalFields=new RelationalFields(element);
					if(isParentNative)
					{
						sl.Add(relationalFields.Native,relationalFields);
					}
					else
					{
						sl.Add(relationalFields.Referenced,relationalFields);
					}
				}

				relationalFieldsArray =(Relation.RelationalFields[])
					new ArrayList(sl.Values).ToArray(typeof(Relation.RelationalFields));
				

			}

			public string ReferencedEntityName
			{
				get
				{
					return this["name"];
				}
			}
			public Entity ReferencedEntity
			{
				get
				{
					return entity.Collection[ReferencedEntityName];
				}
			}
			public Entity NativeEntity
			{
				get
				{
					return entity;
				}
			}
			public Entity.Field[] ReferencedKey
			{
				get
				{
					Entity.Field[] fields=new Field[relationalFieldsArray.Length];
					for(int i=0; i<fields.Length; i++)
					{
						fields[i]=ReferencedEntity.Fields[relationalFieldsArray[i].Referenced]; 
					}
					return fields;
				}
			}
				
			public Entity.Field[] NativeKey
			{
				get
				{
					
					Entity.Field[] fields=new Field[relationalFieldsArray.Length];
					for(int i=0; i<fields.Length; i++)
					{
						fields[i]=NativeEntity.Fields[relationalFieldsArray[i].Native]; 
					}
					return fields;
				}
			}

			public string this [string key]
			{
				get
				{
					return (xmlElement.Attributes[key]==null)? string.Empty:
						xmlElement.Attributes[key].Value;
				}
			}


		}

		public class RelationCollection:Hashtable
		{
			public RelationCollection(XmlElement xmlElement,Entity nativeEntity,bool isParentNative)
			{
				if(xmlElement==null)
				{
					return;
				}

				//load relations.
				XmlNodeList nodes=XmlUtil.GetNodes(xmlElement,
					"shk:entityReference[@name]",Configurations.Provider.XMLN,"shk");  
				
				foreach(XmlElement element in nodes)
				{
					Relation relation=new Relation(element,nativeEntity,isParentNative);
					base.Add(relation.ReferencedEntityName,relation);
				}
			}
	

			/// <summary>
			/// Add
			/// </summary>
			/// <param name="key"></param>
			/// <param name="value"></param>
			public override void Add(object key, object value)
			{
				throw Logger.Write(new Exception("Obsolete method"));
			}
		
		
			/// <summary>
			/// Indexer
			/// </summary>
			public Entity.Relation this[string key]
			{
				get
				{
					return base[key] as Entity.Relation; 
				}
			}
		}

		
		#endregion

		#region Inner Class [Field]
		public class Field
		{
			XmlElement xmlElement;
			Entity entity;
			public Field(XmlElement xmlElement,Entity entity)
			{
				this.xmlElement=xmlElement;
				this.entity=entity;
			}


			public string DataType
			{
				get
				{
					return this["datatype"];
				}
			}
			public bool IsNative
			{
				get
				{
					return (this["type"]=="native")? true: false;
				}
			}
			public string TokenId
			{
				get
				{
					return this["tokenid"];
				}
			}
			public bool IsRange
			{
				get
				{
					return IsValueType && DataType != Constants.UDTypes.BIT; 

				}
			}

			public bool IsValueType
			{
				get
				{
					Type type=Type.GetType(string.Format("System.{0}",
						FreeHash.CSharpSystemType[DataType]));
					return type.IsValueType;
				}
			}

			public bool IsKey
			{
				get
				{
					return (this["key"]=="true")? true: false;
				}
			}
			public bool IsUnique
			{
				get
				{
					return (this["unique"]=="true")? 
						true:
						false;
				}
			}
			
			public bool AutoIncreament
			{
				get
				{
					return (this["computed"]=="true")? true: false;
				}
			}

			public bool TokenEnabled
			{
				get
				{
					return (this["autoGenerated"]=="true")? true: false;
				}
			}

			public bool Nullable
			{
				get
				{
					return (this["nullable"]=="true")? true: false;
				}
			}

			public string Name
			{
				get
				{
					if(xmlElement.Attributes["name"]== null)
					{
						throw Logger.Write(new Exception("Entity field name should be defined"));
					}
					return xmlElement.Attributes["name"].Value;
					
				}
			}

			public string DBFiledName
			{
				get
				{
					if(xmlElement.Attributes["dbFieldName"] != null)
					{
						return xmlElement.Attributes["dbFieldName"].Value;
					}
					else
					{
						return Name;
					}
				}
			}
			

			public Entity RelatedEntity
			{
				get
				{
					return entity;
				}
			}
			public string this [string key]
			{
				get
				{
					if(entity.OverridingEntity != null && 
						entity.OverridingEntity.Fields[Name] != null && 
							entity.OverridingEntity.Fields[Name][key] != string.Empty)
					{
						return entity.OverridingEntity.Fields[Name][key];
					}
					else
					{
						return (xmlElement.Attributes[key]==null)? string.Empty:
							xmlElement.Attributes[key].Value;
					}
				}
			}

			public string this[string key, bool isAttributeList]
			{
				get
				{
					string str=this[key ];
					if(!isAttributeList)
					{
						return str;
					}
					else
					{
						return StringProcessor.GetFormatedKeyValuePairs(str);	
					}
				}
			}

			//****Custom Entity Properties
			#region Custom Entity Properties
			public string FormatString
			{
				get
				{
					string str=this["formatString"];
					if(str==string.Empty)
					{
						//default
						str= "{0}";
					}
					return str;
				}
			}

			public string Caption
			{
				get
				{
					string str=this["caption"];
					if(str==string.Empty)
					{
						//default
						str= StringProcessor.Caption(Name);
					}
					return str;
				}
			}

			public string ListTable
			{
				get
				{
					
					string str=this["listTable"];
					if(str==string.Empty && ListTableDefaultUsed)
					{
						//default
						foreach(string key in entity.Parents.Keys)
						{
							Relation rel= entity.Parents[key];
							if(rel.NativeKey.Length==1 && rel.NativeKey[0].Name == DBFiledName)
							{
								//if this field is foriegn key with single field relation.
								//then get parent table name.
								str = rel.ReferencedEntityName; 
								break;
							}

						}
					}
					return str;
				}
			}

			public string ListTableDisplayFields
			{
				get
				{
					string str=this["listTableDisplayFields"];
					if(str==string.Empty)
					{
						//default
						return ListTableKeyField;
					}
					return str;
				}
			}

			public string ListControl
			{
				get
				{
					string str=this["listControl"];
					if(str==string.Empty)
					{
						//default
						if(ListTable != string.Empty)
						{
							return "ASCXCombo";
						}
					}
					return str;
				}
			}

			public string WebControl
			{
				get
				{
					string str=this["webControl"];
					if(str==string.Empty)
					{
						//default
						if(DataType == Constants.UDTypes.DATE_TIME)
						{
							return "ASCXDatePicker";
						}
						else if(IsRange && DataType != Constants.UDTypes.BIT)
						{
							return "ASCXNumberBox";
						}
						else if (DataType == Constants.UDTypes.BIT)
						{
							return "ASCXCheckBox";
						}
						else
						{
							return "ASCXTextBox";
						}
					}
					return str;
				}
			}
			
			public string ListTableKeyField
			{
				get
				{
					string str=this["listTableKeyField"];

					//default
					if(str==string.Empty)
					{
						string listTable = ListTable;
						if(listTable != string.Empty)
						{
							if(entity.Parents.ContainsKey(listTable))
							{
								return entity.Parents[listTable].ReferencedKey[0].Name;
							}
						}
					}
					return str;
				}
			}

			public bool UsingRange
			{
				get
				{
					string str=this["usingRange"];
					bool val = false;
					//default
					if(str==string.Empty)
					{
						string listTable = ListTable;
						if(listTable == string.Empty && IsRange && DataType != "bit" )
						{
							val = true;
						}
					}
					else
					{
						val = (str=="true")? true: false;
					}
					return val;
				}
			}

			public int Ordinal
			{
				get
				{
					int i=0;
					try
					{
						i=Convert.ToInt32(this["ordinal"]);
					}
					catch{}
					return i;
				}
			}

			public bool Displayed
			{
				get
				{
					string str=this["displayed"];
					bool val = (str=="false")? false: true; //default is true.
					return val;

				}
			}

			public bool ReadOnly
			{
				get
				{
					string str=this["readonly"];
					bool val = false;
					if(str == "true")
					{
						val = true;
					}
					else if (str == string.Empty)
					{
						if(TokenEnabled || AutoIncreament)
						{
							val = true;
						}
					}

					return val;
				}
			}
			public bool Required
			{
				get
				{
					string str=this["required"];
					bool val = false;
					if(str == "true")
					{
						val = true;
					}
					else if (str == string.Empty)
					{
						if(TokenEnabled || AutoIncreament)
						{
							val = false;
						}
						val = !Nullable;
						
					}

					return val;

				}
			}
			public bool UsingTime
			{
				get
				{
					string str=this["usingTime"];
					bool val = (str=="true")? true: false; //default is false.
					return val;

				}
			}

			private bool ListTableDefaultUsed
			{
				get
				{
					string str=this["listTableDefaultUsed"];
					bool val = (str=="false")? false: true; //default is true.
					return val;

				}
			}


			public bool ConfirmationRequired
			{
				get
				{
					string str=this["confirmationRequired"];
					bool val = (str=="true")? true: false; //default is false.
					return val;

				}
			}
			
			public string ASPXAttributes
			{
				get
				{
					return this["aspxAttributes",true];
				}
			}

			#endregion


		}

		public class FieldCollection:Hashtable
		{
			private Entity entity;
			SortedList sl=new SortedList();
			public FieldCollection(Entity entity)
			{
				//just sort filelds of entity,no need to create new fields.
				this.entity=entity;
				
		
				//make sorted list
				foreach(string key  in entity.Fields.Keys)
				{
				    Field field = entity.Fields[key];
					string sortedKey = field.Ordinal.ToString().PadLeft(5,'0') + field.Name;
					sl.Add(sortedKey  ,field);
				}

				//copy sorted list to collection.
				foreach(string key in sl.Keys)
				{
					Field field=sl[key] as Field; 
					base.Add(field.Name,field);
				}
			}
				

			public FieldCollection(XmlElement xmlElement,Entity entity)
			{
				this.entity=entity;
				
				//load fields.
				XmlNodeList nodes=XmlUtil.GetNodes(xmlElement,
					"shk:fields/shk:field[@name]",Configurations.Provider.XMLN,"shk");  
				
				
				//make sorted list
				foreach(XmlElement element in nodes)
				{
					Field field=new Field(element,entity);
					sl.Add(field.Ordinal.ToString().PadLeft(5,'0') + field.Name ,field);
				}

				//copy sorted list to collection.
				foreach(string key in sl.Keys)
				{
					Field field=sl[key] as Field; 
					base.Add(field.Name,field);
				}

				
			}
				

			/// <summary>
			/// Add
			/// </summary>
			/// <param name="key"></param>
			/// <param name="value"></param>
			public override void Add(object key, object value)
			{
				throw Logger.Write(new Exception("Obsolete method"));
			}
		
		
			/// <summary>
			/// Indexer
			/// </summary>
			public Entity.Field this[string key]
			{
				get
				{
					return base[key] as Entity.Field; 
				}
			}
			public Entity.Field this[int index]
			{
				get
				{
					return sl.GetByIndex(index) as Entity.Field; 
				}
			}
			
			#endregion

		}

	}
	public class EntityCollection:Hashtable
	{
		private Hashtable htAccessors = new Hashtable();

		private Hashtable htTypeCodesSortedList=new Hashtable();//used for custom entities.
		private Hashtable htTypeCodesList=new Hashtable();//used for custom entities.
		//each codeType contains sortedList and hashtable objects in htTypeCodesSortedList and htTypeCodesList
		//we have to keep both because sortedlist contains keys with the prefix of ordinal.
		//and user cant get exact entity.
		
		public EntityCollection(XmlDocument xmlDoc,XmlDocument xmlDocCustom)
		{
			//load entities.
			XmlNodeList nodes=XmlUtil.GetNodes(xmlDoc.DocumentElement,
				"shk:entity[@name]",Configurations.Provider.XMLN,"shk");  
				
			foreach(XmlElement element in nodes)
			{
				Entity entity=new Entity(element,this);
				base.Add(entity.Name,entity);
				
    			//for getting accessors list.
				if(entity.AccessorName != string.Empty && !htAccessors.ContainsKey(entity.AccessorName))
				{
					htAccessors.Add(entity.AccessorName,entity.AccessorName);
				}
			}


			//load custom entities.
			nodes=XmlUtil.GetNodes(xmlDocCustom.DocumentElement,
				"shk:entity[@name]",Configurations.Provider.XMLN,"shk");  
			foreach(XmlElement element in nodes)
			{
				Entity entity=new Entity(element,this);
				if(entity.TypeCode==string.Empty)
				{
					//simply ignore.
					continue;
				}

				
				if(!htTypeCodesList.ContainsKey(entity.TypeCode))
				{
					//create new list and add it into htTypeCodes hashtable.
					htTypeCodesList.Add(entity.TypeCode,new Hashtable());
					htTypeCodesSortedList.Add(entity.TypeCode,new SortedList());

				}
				
				SortedList slCustomEnties=htTypeCodesSortedList[entity.TypeCode] as SortedList;
				Hashtable htCustomEnties=htTypeCodesList[entity.TypeCode] as Hashtable  ;
				string keySorted=GetCustomEntityKeySorted(entity);

				if(htCustomEnties.ContainsKey(entity.Name))
				{
					throw new Exception(string.Format("Entity with the name [{0}] already exists for the type code [{1}]",
						entity.Name,entity.TypeCode));
				}

				slCustomEnties.Add(keySorted,entity);
				htCustomEnties.Add(entity.Name,entity);
				
			}
		}

		private string GetCustomEntityKeySorted(Entity entity)
		{
			return GetCustomEntityKeySorted(entity.Name,entity.Ordinal); 
		}
		private string GetCustomEntityKeySorted(string entityName,int entityOrdinal)
		{
			return entityOrdinal.ToString().PadLeft(5,'0')+ entityName;
		}
	

		/// <summary>
		/// Add
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public override void Add(object key, object value)
		{
			throw Logger.Write(new Exception("Obsolete method"));
		}
		
		
		/// <summary>
		/// Indexer
		/// </summary>
		public Entity this[string key]
		{
			get
			{
				return base[key] as Entity; 
			}
		}

		public Entity this[string typeCode,string entityName]
		{
			get
			{
				Entity entity=null;
				if(htTypeCodesList.ContainsKey(typeCode))
				{
					entity = ((Hashtable)htTypeCodesList[typeCode])[entityName] as Entity; 
				}
				return entity;
			}
		}

		public Entity this[string typeCode,int index]
		{
			get
			{
				Entity entity=null;
				if(htTypeCodesList.ContainsKey(typeCode))
				{
					entity = ((SortedList)htTypeCodesSortedList[typeCode]).GetByIndex(index) as Entity ; 
				}
				return entity;
			}
		}

		public int GetCount(string typeCode)
		{
			if(htTypeCodesList.ContainsKey(typeCode))
			{
				return ((Hashtable)htTypeCodesList[typeCode]).Count; 
			}
			else
			{
				return 0;
			}
			
		}

		public string[] Accessors
		{
			get
			{
				return new ArrayList(htAccessors.Keys).ToArray(typeof(string)) as string[];
			}
		}
	}
	internal class EntitySorter:IComparer
	{

	
		#region IComparer Members

		public int Compare(object x, object y)
		{
			return 0;
		}

		#endregion
	}

}
